package com.example.springbooteasycode.zjrc.project.Koal.CToJavaParser;

import java.util.*;

public class TLVParser {
    // 存储文档定义的标签描述和长度规则
    private static final Map<String, FieldMeta> FIELD_METADATA = new HashMap<>();

    static {
        // 初始化文档中定义的标签元数据
        FIELD_METADATA.put("9F02", new FieldMeta("授权金额", 8, "金额类", "BCD"));
        FIELD_METADATA.put("9F03", new FieldMeta("其它金额", 8, "金额类", "BCD"));
        FIELD_METADATA.put("9F1A", new FieldMeta("终端国家代码", 4, "数值类", "BCD"));
        FIELD_METADATA.put("95", new FieldMeta("终端验证结果", 10, "二进制类", "HEX"));
        FIELD_METADATA.put("5F2A", new FieldMeta("交易货币代码", 4, "数值类", "BCD"));
        FIELD_METADATA.put("9A", new FieldMeta("交易日期", 6, "日期类", "BCD"));
        FIELD_METADATA.put("9C", new FieldMeta("交易类型", 2, "枚举类", "HEX"));
        FIELD_METADATA.put("9F37", new FieldMeta("不可预知数", 8, "随机数", "HEX"));
        FIELD_METADATA.put("82", new FieldMeta("应用交互特征(AIP)", 4, "二进制类", "HEX"));
        FIELD_METADATA.put("9F36", new FieldMeta("应用交易计数器(ATC)", 4, "计数器", "HEX"));
        FIELD_METADATA.put("9F10", new FieldMeta("卡片验证结果(CVR)", 10, "二进制类", "HEX"));
        FIELD_METADATA.put("9F26", new FieldMeta("应用密文(AC)", 16, "加密类", "HEX"));
        FIELD_METADATA.put("9F34", new FieldMeta("卡交易 qualifier(CTQ)", 6, "二进制类", "HEX"));
        FIELD_METADATA.put("9F33", new FieldMeta("终端能力", 6, "二进制类", "HEX"));
        FIELD_METADATA.put("9F35", new FieldMeta("终端类型", 2, "枚举类", "HEX"));
        FIELD_METADATA.put("9F41", new FieldMeta("交易序列号", 8, "序列号", "HEX"));
    }

    public static void main(String[] args) {
        // 替换为实际的金融报文中的数据域部分（去除前后非数据域内容）
        String dataHex = "9F02060000000010009F03060000000000009F1A0201565F2A0201569A032208059C01009F3704211102009F3602080082025C009F101307010103A0B0000000000000000000950500000000009F2608FD577C00004003A0";

        try {
            // 解析 TLV 数据
            List<TLV> tlvList = parseTLV(dataHex);

            // 输出解析结果
            System.out.println("===== TLV 解析结果 =====");
            for (TLV tlv : tlvList) {
                System.out.println(tlv);

                // 根据文档规则验证字段
                validateField(tlv);

                // 按类型转换值
                System.out.println("  转换后值: " + convertValue(tlv));
                System.out.println();
            }

            // 检查是否有未定义的标签
            checkUndefinedTags(tlvList);
        } catch (Exception e) {
            System.err.println("解析失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 解析 TLV 格式数据
     */
    public static List<TLV> parseTLV(String dataHex) {
        List<TLV> result = new ArrayList<>();
        int pos = 0;

        while (pos < dataHex.length()) {
            // 1. 解析标签 (Tag)
            String tag = parseTag(dataHex, pos);
            pos += tag.length();

            // 2. 解析长度 (Length)
            int length = parseLength(dataHex, pos);
            pos += getLengthFieldLength(length) * 2; // 长度字段占的 Hex 字符数

            // 3. 解析值 (Value)
            String value = dataHex.substring(pos, pos + length * 2);
            pos += length * 2;

            // 4. 创建 TLV 对象
            TLV tlv = new TLV(tag, length, value);
            result.add(tlv);
        }

        return result;
    }

    /**
     * 解析标签 (Tag)
     */
    private static String parseTag(String dataHex, int pos) {
        // 检查标签是否为复合标签（第 5 位为 1）
        String firstByte = dataHex.substring(pos, pos + 2);
        int firstByteValue = Integer.parseInt(firstByte, 16);

        if ((firstByteValue & 0x1F) == 0x1F) {
            // 复合标签，占 2 字节
            return dataHex.substring(pos, pos + 4);
        } else {
            // 简单标签，占 1 字节
            return dataHex.substring(pos, pos + 2);
        }
    }

    /**
     * 解析长度 (Length)
     */
    private static int parseLength(String dataHex, int pos) {
        String lengthHex = dataHex.substring(pos, pos + 2);
        int length = Integer.parseInt(lengthHex, 16);

        // 检查是否为扩展长度格式
        if ((length & 0x80) != 0) {
            // 扩展长度格式，后续字节表示实际长度
            int bytesToRead = length & 0x7F;
            lengthHex = dataHex.substring(pos + 2, pos + 2 + bytesToRead * 2);
            length = Integer.parseInt(lengthHex, 16);
        }

        return length;
    }

    /**
     * 获取长度字段占用的字节数
     */
    private static int getLengthFieldLength(int length) {
        if (length <= 127) {
            return 1; // 单字节长度
        } else if (length <= 255) {
            return 2; // 2字节长度 (0x81 + 实际长度)
        } else if (length <= 65535) {
            return 3; // 3字节长度 (0x82 + 实际长度)
        }
        throw new IllegalArgumentException("不支持的长度值: " + length);
    }

    /**
     * 验证字段是否符合文档规则
     */
    private static void validateField(TLV tlv) {
        FieldMeta meta = FIELD_METADATA.get(tlv.tag);
        if (meta == null) {
            System.out.println("  警告: 未知标签 " + tlv.tag);
            return;
        }

        // 验证长度
        if (tlv.length != meta.expectedLength / 2) {
            System.out.println("  错误: 标签 " + tlv.tag + " 长度不匹配! 预期 " +
                    (meta.expectedLength / 2) + " 字节, 实际 " + tlv.length + " 字节");
        }

        // 特定字段验证逻辑
        if ("9F1A".equals(tlv.tag)) {
            // 验证国家代码 (中国: 156)
            String countryCode = convertBCDToStr(tlv.value);
            if (!"156".equals(countryCode)) {
                System.out.println("  警告: 国家代码非中国 (156), 实际值: " + countryCode);
            }
        } else if ("9C".equals(tlv.tag)) {
            // 验证交易类型
            Map<String, String> txnTypes = new HashMap<>();
            txnTypes.put("00", "购买");
            txnTypes.put("01", "现金支取");
            txnTypes.put("20", "余额查询");

            if (!txnTypes.containsKey(tlv.value)) {
                System.out.println("  警告: 未知交易类型: " + tlv.value);
            } else {
                System.out.println("  交易类型: " + txnTypes.get(tlv.value));
            }
        }
    }

    /**
     * 根据字段类型转换值格式
     */
    private static String convertValue(TLV tlv) {
        FieldMeta meta = FIELD_METADATA.get(tlv.tag);
        if (meta == null) {
            return tlv.value;
        }

        switch (meta.encoding) {
            case "BCD":
                return convertBCDToStr(tlv.value);
            case "AMOUNT":
                return convertAmount(tlv.value);
            case "DATE":
                return convertDate(tlv.value);
            default:
                return tlv.value;
        }
    }

    /**
     * BCD 编码转字符串
     */
    private static String convertBCDToStr(String bcdHex) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < bcdHex.length(); i += 2) {
            String hexByte = bcdHex.substring(i, i + 2);
            int value = Integer.parseInt(hexByte, 16);
            result.append(String.format("%02d", value));
        }
        return result.toString();
    }

    /**
     * 转换金额字段
     */
    private static String convertAmount(String amountHex) {
        String bcdStr = convertBCDToStr(amountHex);
        // 金额通常为分，转换为元
        return String.format("%.2f", Double.parseDouble(bcdStr) / 100);
    }

    /**
     * 转换日期字段
     */
    private static String convertDate(String dateHex) {
        String bcdStr = convertBCDToStr(dateHex);
        // 格式: YYMMDD
        return bcdStr.substring(0, 2) + "-" +
                bcdStr.substring(2, 4) + "-" +
                bcdStr.substring(4, 6);
    }

    /**
     * 检查是否有未定义的标签
     */
    private static void checkUndefinedTags(List<TLV> tlvList) {
        Set<String> undefinedTags = new HashSet<>();
        for (TLV tlv : tlvList) {
            if (!FIELD_METADATA.containsKey(tlv.tag)) {
                undefinedTags.add(tlv.tag);
            }
        }

        if (!undefinedTags.isEmpty()) {
            System.out.println("===== 未定义的标签 =====");
            for (String tag : undefinedTags) {
                System.out.println("  " + tag);
            }
            System.out.println("请检查文档是否遗漏定义这些标签!");
        }
    }

    /**
     * TLV 数据结构
     */
    static class TLV {
        String tag;
        int length;
        String value;

        public TLV(String tag, int length, String value) {
            this.tag = tag;
            this.length = length;
            this.value = value;
        }

        @Override
        public String toString() {
            FieldMeta meta = FIELD_METADATA.get(tag);
            String desc = meta != null ? meta.description : "未知标签";

            return "标签: " + tag + " (" + desc + ")\n" +
                    "长度: " + length + " 字节\n" +
                    "值: " + value;
        }
    }

    /**
     * 字段元数据
     */
    static class FieldMeta {
        String description;
        int expectedLength; // 单位: Hex 字符数
        String fieldType;
        String encoding;

        public FieldMeta(String description, int expectedLength, String fieldType, String encoding) {
            this.description = description;
            this.expectedLength = expectedLength;
            this.fieldType = fieldType;
            this.encoding = encoding;
        }
    }
}
